#include "../include/mytools.h"


void parse_xml(char *back_str, const char *xml_str, const char *pre_str, const char *suf_str)
{
	if (DEBUG_LOG)
		printf("L(%d) xml_str:%s pre_str:%s suf_str:%s \n", __LINE__, xml_str, pre_str, suf_str);
	int offset = strlen(pre_str);
	char *ptr_first = strstr(xml_str, pre_str);
	char *ptr_end = strstr(xml_str, suf_str);

	if ((ptr_first != NULL) && (ptr_end != NULL))
	{
		ptr_first = ptr_first + offset;
		strncpy(back_str, ptr_first, (ptr_end - ptr_first));
		if (DEBUG_LOG)
			printf("L(%d) xml data back_str:%s\n", __LINE__, back_str);
	}
}

bool recvfile(int sockfd, char *path, char* sourcefile_name, char * file_name, char *buffer, long file_size)
{
	printf("start downloading %s...\n", sourcefile_name);
	int file_fp;
	long recv_sum = 0;		  //统计接收文件总大小
	int progress = 50;		  //进度条的长度
	int current = 0;		  //当前进度
	// 打开文件，准备写入
	char newfile_name[MAX_SIZE_OF_FILE_NAME]={0};
	strcpy(newfile_name, path);
	if(newfile_name[0] != '\0' && strlen(newfile_name) == 1)
	{
		if(newfile_name[0] == '.')
			newfile_name[0] = '\0';
		else if(newfile_name[0] != '/')
			strcat(newfile_name, "/");
	}
	else strcat(newfile_name, "/");
	strcat(newfile_name, file_name);
	//printf("create file: %s\n", newfile_name);
	file_fp = open(newfile_name, O_CREAT | O_RDWR, 0777);
	if (file_fp < 0)
	{
		printf("file: %s file: %scan not open to write.\n", newfile_name, file_name);
		return false;
	}
	int length = 0;
	int hasrecv = 0;
	while(file_size > 0 && (length = recv(sockfd, buffer, BUFFER_SIZE, 0)) > 0) 
    { 
        if( write( file_fp, buffer, length ) < length) 
        { 
            printf("file: %s write failed\n", newfile_name); 
			close(file_fp);
            return false; 
        } 
		recv_sum += length; //累计接收文件大小
		hasrecv += length;
		//计算当前进度
		current = recv_sum * progress / file_size;
		//打印进度条
		
		printf("\r");
		printf("[");
		for (int i = 0; i < progress; i++)
		{
			if (i < current)
				printf("="); //已接收部分
			else
				printf("+"); //未接收部分
		}
		printf("]");
		printf(" %8ld/%ld %6.2f%%", recv_sum, file_size, (float)recv_sum / file_size * 100);
		
        memset( buffer,0, BUFFER_SIZE );
		if(hasrecv > MAX_SENDLENGTH)
		{
			if (send(sockfd, "#beginsend#", 11, 0) < 0)
				printf("\nfile: %s send #beginsend# failed.\n", newfile_name);
			hasrecv = 0;
		}
		if(recv_sum >= file_size) break;
    } 
	if (send(sockfd, "#successful#", 12, 0) < 0)
		printf("\nfile: %s send #successful# failed.\n", newfile_name);
	// 接收成功后，关闭文件，关闭socket 
    printf("\nOK %s.\n", newfile_name); 
    close(file_fp); 
	return true;	
}

bool recvdir(int sockfd, char *path, char * sourcefile_name, char * file_name, char *buffer)
{
	//创建文件夹
	printf("start downloading %s...\n", sourcefile_name);
	char newfile_path[MAX_SIZE_OF_FILE_NAME] = {0};
	strcpy(newfile_path, path);
	if(newfile_path[0] != '\0' && strlen(newfile_path) == 1)
	{
		if(newfile_path[0] == '.')
			newfile_path[0] = '\0';
		else if(newfile_path[0] != '/')
			strcat(newfile_path, "/");
	}
	else strcat(newfile_path, "/");
	strcat(newfile_path, file_name);
	int mkflag = mkdir(newfile_path, 0777);
	if(mkflag < 0)
	{
		perror("mkdir: ");
		return false;
	}
	//接收头部消息
	bool recvflag = true;
	while(1)
	{
		int length = 0; 
		memset( buffer, 0, BUFFER_SIZE);
		length = recv(sockfd, buffer, BUFFER_SIZE, 0);
		if (length < 0)
		{
			perror("server recieve data failed: ");
			return false;
		}
		//printf("%s\n", buffer);
		if(!strncmp(buffer, "#enddir#", 8)){ break;}
		if(send(sockfd, "#over#", 6, 0) < 0)
		{
			printf("#over# recv error.\n");
			exit(1);
		}
		struct TCP_STRUCT_DATA struct_data;
		memset(&struct_data, 0x0, sizeof(struct_data));
		memcpy(&struct_data, buffer, sizeof(struct_data)); 
		long file_size = 0;
		char newfile_name[MAX_SIZE_OF_FILE_NAME] = {0};
		if (struct_data.m_data_len > 0) // m_data_len>0 则仍有数据要接收
		{
			length = recv(sockfd, buffer, struct_data.m_data_len, 0);
			if (length < 0)
			{
				perror("server recieve data failed: ");
				return false;
			}
			
			//解析xml
			char file_size_tmp[8] = {0};
			parse_xml(newfile_name, buffer, "<name>", "</name>");
			parse_xml(file_size_tmp, buffer, "<size>", "</size>");
			//printf("newfile_name: %s.\n", newfile_name);
			if (newfile_name[0] != '\0')
				file_size = atol(file_size_tmp);
			else{
				printf("parse xml error！\n");
				return false;
			}
			if(send(sockfd, "#over#", 6, 0) < 0)
			{
				printf("#over# recv error.\n");
				exit(1);
			}
		}
		
		if(struct_data.m_cmd == GET_DIR)
		{
			recvflag = recvdir(sockfd, newfile_path, newfile_name, newfile_name, buffer);
		}
		else if(struct_data.m_cmd == GET_FILE)
		{
			recvflag = recvfile(sockfd, newfile_path, newfile_name, newfile_name, buffer, file_size);
		}
		if(!recvflag)
		{
			printf("failed to download %s.\n", newfile_path);
			return false;
		}
		
	}
	if (send(sockfd, "#dirsuccessful#", 15, 0) < 0)
		printf("file: %s send #successful# failed.\n", file_name);
	printf("OK %s.\n", newfile_path); 
	return true;
}

int recvfromserver(int sockfd, Popt opt)
{ 
	
	long file_size = 0;		  //文件总大小
    char * file_name = opt->sourcename;
	char * out_file_name = opt->outfilename;
   
    char buffer[BUFFER_SIZE]; 
    memset(buffer, 0, BUFFER_SIZE );
    strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name)); 
     
    // 向服务器发送buffer中的数据 
    if(send(sockfd, buffer, BUFFER_SIZE, MSG_NOSIGNAL) < 0) 
    { 
       perror("send file name failed:"); 
       exit(1); 
    } 
	if(is_exist_file(".", out_file_name) >= 0)
	{
		printf("failed to download %s: %s already exists.\n", out_file_name, out_file_name);
		exit(1);
	}
    // 从服务器接收数据到buffer中 
    
    int length = 0; 
	memset( buffer, 0, BUFFER_SIZE);
    length = recv(sockfd, buffer, BUFFER_SIZE, 0);
	if(strncmp(buffer, "#file not found#", 16) == 0)
	{
		printf("failed to download %s: %s is not exists.\n", file_name, file_name);
		exit(1);
	}
	struct TCP_STRUCT_DATA struct_data;
	memset(&struct_data, 0x0, sizeof(struct_data));
	memcpy(&struct_data, buffer, sizeof(struct_data)); //把结构体的数据通过memcpy的方式拷贝到struct_data中
	//printf("cmd:%d data_len:%d\n", struct_data.m_cmd, struct_data.m_data_len);
	memset( buffer, 0, BUFFER_SIZE);
	if(send(sockfd, "#over#", 6, 0) < 0)
	{
		printf("#over# send error.\n");
		exit(1);
	}
	if (struct_data.m_data_len > 0) // m_data_len>0 则仍有数据要接收
	{
		length = recv(sockfd, buffer, struct_data.m_data_len, 0);
		if (length < 0)
		{
			printf("server recieve data failed.");
			return -1;
		}
		//解析xml
		
		char file_size_tmp[8] = {0};
		parse_xml(file_name, buffer, "<name>", "</name>");
		parse_xml(file_size_tmp, buffer, "<size>", "</size>");
		if (NULL != file_name)
			file_size = atol(file_size_tmp);
		else
		{
			printf("parse xml error！\n");
			return -1;
		}
		
		if(send(sockfd, "#over#", 6, 0) < 0)
		{
			printf("#over# send error.\n");
			exit(1);
		}
	}
	bool recvflag = true;
	
	if(struct_data.m_cmd == GET_DIR)
	{
		recvflag = recvdir(sockfd, ".", file_name, out_file_name, buffer);
	}
	else if(struct_data.m_cmd == GET_FILE)
	{
		recvflag = recvfile(sockfd, ".", file_name, out_file_name, buffer, file_size);
	}
	if(!recvflag)
	{
		return -1;
	}
	return 0;
	
}

void outpathfile(int sockfd, Popt opt)
{
	char buffer[BUFFER_SIZE] = {0};
	int length = 0; 
	if(send(sockfd, "#beginpath#", 11, 0) < 0)
	{
		printf("#beginpath# recv error.\n");
		exit(1);
	}
	memset(buffer, 0, BUFFER_SIZE);
	length = recv(sockfd, buffer, BUFFER_SIZE, 0);
	if (length < 0)
	{
		perror("server recieve data failed: ");
		exit(1);
	}
	
	if(send(sockfd, "#over#", 6, 0) < 0)
	{
		printf("#over# recv error.\n");
		exit(1);
	}
	struct TCP_STRUCT_DATA struct_data;
	memset(&struct_data, 0x0, sizeof(struct_data));
	memcpy(&struct_data, buffer, sizeof(struct_data)); 
	long totolsize = struct_data.m_data_len;
	printf("file lists:\n");
	
	int sumlength = 0; 
	int hasrecv = 0;
	memset(buffer, 0, BUFFER_SIZE);
    while((length = recv(sockfd, buffer, BUFFER_SIZE-1, 0)) > 0)
	{
		sumlength += length;
		hasrecv += length;
		if(opt->show)
			printf("%s" ,buffer);
		
		memset(buffer, 0, BUFFER_SIZE);
		if(hasrecv > MAX_SENDLENGTH)
		{
			if (send(sockfd, "#beginsend#", 11, 0) < 0)
				printf("file: %s send #beginsend# failed.\n", opt->sourcename);
			hasrecv = 0;
		}
		if(sumlength >= totolsize)break;
	}
	if(send(sockfd, "#pathover#", 10, 0) < 0)
	{
		printf("#pathover# recv error.\n");
		exit(1);
	}
}

int main(int argc, char *argv[])
{
	//解析参数
	Opt opt;
	memset(&opt , 0, sizeof(Opt));
	opt.show = true;
	strcpy(opt.ip, "localhost");
	opt.port = SERVER_PORT;
	struct option long_options[] = 
	{
		{"list", no_argument, 0, 'l'},
		{"server", required_argument, 0, 's'},
		{"port", required_argument, 0, 'p'},
		{"dl", required_argument, 0, 'd'},
		{"output", required_argument, 0, 'o'},
		{"help", no_argument, 0, 'h'},
		{0, 0, 0, 0}};
	char shortopts[] = "ls:p:d:o:h";
	if(!parseopt(argc, argv, &opt, long_options, shortopts, false))
		return 0;
	if(opt.outfilename[0] == '\0')
		strcpy(opt.outfilename, opt.sourcename);
	struct hostent *h;
	//获取服务器信息
	h=gethostbyname(opt.ip);
	if(NULL == h)
	{
		perror("geyhostbyname");
		exit(1);
	}
	//初始化套接字
	int sockfd=socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}
	struct sockaddr_in server_addr;
	server_addr.sin_family=AF_INET;
	server_addr.sin_port=htons(opt.port);
	server_addr.sin_addr=*((struct in_addr *)h->h_addr_list[0]);
	if(-1==connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)))
	{
		printf("failed to connect to %s:%d\n", opt.ip, opt.port);
		exit(1);
	}
	if(opt.sourcename[0] == '\0')
	{
		outpathfile(sockfd, &opt);
		return 0;
	}
	
	if(recvfromserver(sockfd, &opt) == -1)
		exit(1);
	return 0;
	
}



